home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
-
- /*
- $Header: b1nuA.c,v 1.4 85/08/22 16:50:25 timo Exp $
- */
-
- /* Approximate arithmetic */
-
- #include "b.h"
- #include "b0con.h"
- #include "b1obj.h"
- #include "b1num.h"
- #include "b3err.h" /* For still_ok */
-
- /*
- For various reasons, on some machines (notably the VAX), the range
- of the exponent is too small (ca. 1.7E38), and we cope with this by
- adding a second word which holds the exponent.
- However, on other machines (notably the IBM PC), the range is sufficient
- (ca. 1E300), and here we try to save as much code as possible by not
- doing our own exponent handling. (To be fair, we also don't check
- certain error conditions, to save more code.)
- The difference is made by #defining EXT_RANGE (in b1num.h), meaning we
- have to EXTend the RANGE of the exponent.
- */
-
- #ifdef EXT_RANGE
- Hidden struct real app_0_buf = {Num, 1, -1, 0.0, -(double)Maxint};
- /* Exponent must be less than any realistic exponent! */
- #else !EXT_RANGE
- Hidden struct real app_0_buf = {Num, 1, -1, 0.0};
- #endif !EXT_RANGE
-
- Visible real app_0 = &app_0_buf;
-
- /*
- * Build an approximate number.
- */
-
- Visible real mk_approx(frac, expo) double frac, expo; {
- real u;
- #ifdef EXT_RANGE
- expint e;
- if (frac != 0) frac = frexp(frac, &e), expo += e;
- if (frac == 0.5) frac = 1, --expo; /* Assert 0.5 < frac <= 1 */
- if (frac == 0 || expo < -BIG) return (real) Copy(app_0);
- if (expo > BIG) {
- error(MESS(700, "approximate number too large"));
- expo = BIG;
- }
- #else !EXT_RANGE
- if (frac == 0.0) return (real) Copy(app_0);
- frac= ldexp(frac, (int)expo);
- #endif EXT_RANGE
- u = (real) grab_num(-1);
- Frac(u) = frac;
- #ifdef EXT_RANGE
- Expo(u) = expo;
- #endif EXT_RANGE
- return u;
- }
-
- /*
- * Approximate arithmetic.
- */
-
- Visible real app_sum(u, v) real u, v; {
- #ifdef EXT_RANGE
- real w;
- if (Expo(u) < Expo(v)) w = u, u = v, v = w;
- if (Expo(v) - Expo(u) < Minexpo) return (real) Copy(u);
- return mk_approx(Frac(u) + ldexp(Frac(v), (int)(Expo(v) - Expo(u))),
- Expo(u));
- #else !EXT_RANGE
- return mk_approx(Frac(u) + Frac(v), 0.0);
- #endif !EXT_RANGE
- }
-
- Visible real app_diff(u, v) real u, v; {
- #ifdef EXT_RANGE
- real w;
- int sign = 1;
- if (Expo(u) < Expo(v)) w = u, u = v, v = w, sign = -1;
- if (Expo(v) - Expo(u) < Minexpo)
- return sign < 0 ? app_neg(u) : (real) Copy(u);
- return mk_approx(
- sign * (Frac(u) - ldexp(Frac(v), (int)(Expo(v) - Expo(u)))),
- Expo(u));
- #else !EXT_RANGE
- return mk_approx(Frac(u) - Frac(v), 0.0);
- #endif !EXT_RANGE
- }
-
- Visible real app_neg(u) real u; {
- return mk_approx(-Frac(u), Expo(u));
- }
-
- Visible real app_prod(u, v) real u, v; {
- return mk_approx(Frac(u) * Frac(v), Expo(u) + Expo(v));
- }
-
- Visible real app_quot(u, v) real u, v; {
- if (Frac(v) == 0.0) {
- error(MESS(701, "in u/v, v is zero"));
- return (real) Copy(u);
- }
- return mk_approx(Frac(u) / Frac(v), Expo(u) - Expo(v));
- }
-
- /*
- YIELD log"(frac, expo):
- CHECK frac > 0
- RETURN normalize"(expo*logtwo + log(frac), 0)
- */
-
- Visible real app_log(v) real v; {
- double frac = Frac(v), expo = Expo(v);
- if (frac <= 0) {
- error(MESS(702, "in log x, x is <= 0"));
- return (real) Copy(app_0);
- }
- return mk_approx(expo*logtwo + log(frac), 0.0);
- }
-
- /*
- YIELD exp"(frac, expo):
- IF expo < minexpo: RETURN zero"
- WHILE expo < 0: PUT frac/2, expo+1 IN frac, expo
- PUT exp frac IN f
- PUT normalize"(f, 0) IN f, e
- WHILE expo > 0:
- PUT (f, e) prod" (f, e) IN f, e
- PUT expo-1 IN expo
- RETURN f, e
- */
-
- Visible real app_exp(v) real v; {
- #ifdef EXT_RANGE
- expint ei;
- double frac = Frac(v), expo = Expo(v), new_expo;
- static double canexp;
- if (!canexp) canexp = floor(log(log(Maxreal)) / logtwo);
- if (expo <= canexp) {
- if (expo < Minexpo) return mk_approx(1.0, 0.0);
- frac = ldexp(frac, (int)expo);
- expo = 0;
- }
- else if (expo >= Maxexpo) {
- /* Definitely too big (the real boundary is much smaller
- but here we are in danger of overflowing new_expo
- in the loop below) */
- return mk_approx(1.0, Maxreal); /* Force an error! */
- }
- else {
- frac = ldexp(frac, (int)canexp);
- expo -= canexp;
- }
- frac = exp(frac);
- new_expo = 0;
- while (expo > 0 && frac != 0) {
- frac = frexp(frac, &ei);
- new_expo += ei;
- frac *= frac;
- new_expo += new_expo;
- --expo;
- }
- return mk_approx(frac, new_expo);
- #else !EXT_RANGE
- return mk_approx(exp(Frac(v)), 0.0);
- #endif !EXT_RANGE
- }
-
- /*
- YIELD (frac, expo) power" v":
- \ (f*2**e)**v =
- \ = f**v * 2**(e*v) =
- \ = f**v * 2**((e*v) mod 1) * 2**floor(e*v) .
- PUT exp"(v" prod" normalize"(log frac, 0)) IN temp1" \ = f**v
- PUT expo*numval(v") IN ev \ = e*v
- PUT exp(logtwo * (ev - floor ev))) IN temp2 \ = 2**(ev mod 1)
- PUT temp1" IN f, e
- RETURN normalize"(f*temp2, e + floor ev)
- */
-
- Visible real app_power(u, v) real u, v; {
- double frac = Frac(u);
- #ifdef EXT_RANGE
- real logfrac, vlogfrac, result;
- double expo = Expo(u), rest;
- #endif !EXT_RANGE
- if (frac <= 0) {
- if (frac < 0) error(MESS(703, "in 0**v, v is negative"));
- if (v == app_0) return mk_approx(1.0, 0.0); /* 0**0 = 1 */
- return (real) Copy(app_0); /* 0**x = 0 */
- }
- #ifdef EXT_RANGE
- frac *= 2, expo -= 1; /* Renormalize to 1 < frac <= 2, so log frac > 0 */
- logfrac = mk_approx(log(frac), 0.0);
- vlogfrac = app_prod(v, logfrac);
- result = app_exp(vlogfrac);
- /* But what if result overflows but expo is very negative??? */
- if (still_ok) {
- expo *= numval((value)v);
- rest = expo - floor(expo);
- frac = Frac(result) * exp(logtwo*rest);
- expo = Expo(result) + floor(expo);
- }
- release((value)logfrac), release((value)vlogfrac), release((value)result);
- return mk_approx(frac, expo);
- #else !EXT_RANGE
- return mk_approx(exp(log(frac) * Frac(v)), 0.0);
- #endif !EXT_RANGE
- }
-
- Visible int app_comp(u, v) real u, v; {
- double xu, xv;
- #ifdef EXT_RANGE
- double eu, ev;
- #endif EXT_RANGE
- if (u == v) return 0;
- xu = Frac(u), xv = Frac(v);
- #ifdef EXT_RANGE
- if (xu*xv > 0) {
- eu = Expo(u), ev = Expo(v);
- if (eu < ev) return xu < 0 ? 1 : -1;
- if (eu > ev) return xu < 0 ? -1 : 1;
- }
- #endif EXT_RANGE
- if (xu < xv) return -1;
- if (xu > xv) return 1;
- return 0;
- }
-
- Visible value app_floor(u) real u; {
- #ifdef EXT_RANGE
- integer v, w;
- value twotow, result;
- if (Expo(u) <= Dblbits)
- return (value)
- mk_int(floor(ldexp(Frac(u),
- (int)(Expo(u) < 0 ? -1 : Expo(u)))));
- v = mk_int(ldexp(Frac(u), Dblbits));
- w = mk_int(Expo(u) - Dblbits);
- twotow = power((value)int_2, (value)w);
- result = prod((value)v, twotow);
- release((value) v), release((value) w), release(twotow);
- if (!Integral(result)) syserr(MESS(704, "app_floor: result not integral"));
- return result;
- #else !EXT_RANGE
- return (value) mk_int(floor(Frac(u)));
- #endif !EXT_RANGE
- }
-